/*
  	ledDrive.cpp - an Arduino library for driving led lamps,
	- single LED with fade in and out;
	- RGB LED in single color mode with fade in and out;
	- RGB LED with 5 color rotation with color crossfade     
	- fade is performed with quadratic increment 
	
   Copyright (C) 2014  Gian Luca Rasponi
   
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "ledDrive.h"

ledDrive::ledDrive()
{
}
/* here we drive a single led with fadeIn fadeOut */

void ledDrive::singleColor() {
currentMillis = millis();  
  if (whiteON) {
    target[0] = white[0];
	  } else {
      target[0] = 0;
  }
  
  if (lastTarget[0] != target[0]) {
      ledStartMillis = currentMillis; // take initial time 
      last[0] = ledVal[0];
		if (whiteON) {
      	currFadeTime = (float(fadeIn) * float(abs(last[0] - target[0])) / 255);
		} else {
	      currFadeTime = (float(fadeOut) * float(abs(last[0] - target[0])) / 255);
		}     
		incr[0] = calcIncrement(0);// What's the overall gap?
      elapsedMillis = 0;
      fadeLedNow = 1;
  } else {
	  if ( fadeLedNow ) { // request for fading else fixed
	          elapsedMillis = int( currentMillis - ledStartMillis);
	          if ( elapsedMillis < currFadeTime ) {
	              ledVal[0] = calcLevel(0); // increment             
	            } else {
	                fadeLedNow = 0;
	                ledVal[0] = target[0];
	            } 
	         } else {         
	                ledVal[0] = target[0];
	                }  
  }
  lastTarget[0] = target[0];

}  
void ledDrive::RGBcolor(){
int numberOfColors = 5; // must be 1/3 of next parameter's array
char colorList[18] = { red[0], red[1], red[2], yellow[0], yellow[1], yellow[2], green[0], green[1], green[2], blue[0], blue[1], blue[2], purple[0], purple[1], purple[2]};
currentMillis = millis();

// define which color is on RGB
// start of manual RGB take precedence 

if ( manualRgbON || whiteON) {
   target[0] = white[0];
   target[1] = white[1];
   target[2] = white[2];
   if (!fadeLedNow) {
   	currFadeTime = fadeIn; // white fade IN
   }
   } else {
     if ( rgbON ) {
     		if (rgbON != auxON ) {
	      	currFadeTime = fadeIn; // RGB fade IN
     		}
///color selection here
 
         if (((currentMillis - colorStartMillis) > long(long(colorDuration) * 1000)) || (currentMillis < colorStartMillis)) {  // at end of current color
            colorIndex += 1; // next color 
    		   currFadeTime = crossFade;
        if ( colorIndex > (numberOfColors - 1) ) {
                colorIndex = 0;
            }
            colorStartMillis = currentMillis; // take initial time 
         }   
         target[0] = colorList[(colorIndex*3)];
         target[1] = colorList[(colorIndex*3)+1];
         target[2] = colorList[(colorIndex*3)+2];     
         } else { // fade to black
     			if (rgbON != auxON ) {
	   	   	currFadeTime = fadeOut; // RGB fade OUT
     			}
    			target[0] = 0;
    			target[1] = 0;
    			target[2] = 0;
        } 
   auxON = rgbON;     
   }

// target colors are set according to request    
    
// calculate color sequence and delay     
   
// deploy fade if target color has changed

if ((target[0] != lastTarget[0]) || (target[1] != lastTarget[1]) || (target[2] != lastTarget[2])) {
		currFadeTime = currFadeTime * ((float)(abs(target[0] - lastTarget[0]) + abs(target[1] - lastTarget[1]) + abs(target[2] - lastTarget[2])) / 765);
		last[0] = ledVal[0];
		last[1] = ledVal[1];
		last[2] = ledVal[2];
      ledStartMillis = currentMillis; // take initial time 
		incr[0] = calcIncrement(0);// What's the overall gap?
		incr[1] = calcIncrement(1);// What's the overall gap?
		incr[2] = calcIncrement(2);// What's the overall gap?
		elapsedMillis = 0;	
   	fadeLedNow = 1;
}          
// perform the fade between colors               
if ( fadeLedNow ) { // request for fading else fixed
          elapsedMillis = int( currentMillis - ledStartMillis);
          if ( elapsedMillis < currFadeTime ) {
              ledVal[0] = calcLevel(0); // increment             
              ledVal[1] = calcLevel(1); // increment             
              ledVal[2] = calcLevel(2); // increment             
              } else {
                fadeLedNow = 0;
              } 
         } else {         
		ledVal[0] = target[0];
		ledVal[1] = target[1];
		ledVal[2] = target[2];
	}  
lastTarget[0] = target[0];
lastTarget[1] = target[1];
lastTarget[2] = target[2]; 
}

float ledDrive::calcIncrement(int index) {
	return (float(target[index] - last[index]) / float(currFadeTime));// What's the overall gap?
  }
/* The next function is calculateLevel.
*  the output level is calculated according to elapsed time and gradient.
*  power of two is applied to have smooth response at low values
*/
int ledDrive::calcLevel( int index ) {
  int level = 0;
  int range = target[index] - last[index]; // range from prev to target val
   if ((range == 0) || (incr[index] == 0)) {
     level = target[index]; 
      } else if (range > 0)  {
        level = constrain(int(float(last[index]) + float(pow(((incr[index] * elapsedMillis)/float(sqrt(abs(range)))),2))),0,255);  
      } else {
        int actLevel = int(incr[index] * elapsedMillis) + last[index];
        float sqrtLevel = float((actLevel - target[index])/float(sqrt(abs(range))));
        float powLevel = float(target[index])+float(pow(sqrtLevel,2));
        level = constrain(int(powLevel),0,255);
      }
  return level;
}
